package com.boot.mall.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.boot.common.constant.OrderConstant;
import com.boot.common.enume.OrderStatusEnum;
import com.boot.common.to.HasStockTo;
import com.boot.common.to.order.OrderStatusTo;
import com.boot.common.utils.R;
import com.boot.common.vo.MemberResponseVo;
import com.boot.mall.order.alipay.PayAsyncVo;
import com.boot.mall.order.alipay.PayVo;
import com.boot.mall.order.entity.OrderItemEntity;
import com.boot.mall.order.entity.PaymentInfoEntity;
import com.boot.mall.order.feign.CartFeignService;
import com.boot.mall.order.feign.MemberFeignService;
import com.boot.mall.order.feign.ProductFeignService;
import com.boot.mall.order.feign.WareFeignService;
import com.boot.mall.order.interceptor.LoginInterceptor;
import com.boot.mall.order.service.OrderItemService;
import com.boot.mall.order.service.PaymentInfoService;
import com.boot.mall.order.to.OrderCreateTo;
import com.boot.mall.order.vo.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.boot.mall.order.dao.OrderDao;
import com.boot.mall.order.entity.OrderEntity;
import com.boot.mall.order.service.OrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    // 用于共享提交的订单信息
    private ThreadLocal<OrderSubmitVo> orderSubmitVoThreadLocal = new ThreadLocal<>();

    @Autowired
    private MemberFeignService memberFeignService;
    @Autowired
    private CartFeignService cartFeignService;
    @Autowired
    private WareFeignService wareFeignService;
    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private OrderItemService orderItemService;
    @Autowired
    private PaymentInfoService paymentInfoService;
    @Autowired
    private ThreadPoolExecutor executor;    //自定义线程池

    /**
     * 查询订单确定信息
     * @return
     * @throws ExecutionException
     * @throws InterruptedException
     */
    @Override
    public OrderConfirmVo getConfirmOrder() throws ExecutionException, InterruptedException {
        OrderConfirmVo confirmVo = new OrderConfirmVo();
        // 获取当前线程对象的用户信息
        MemberResponseVo loginMember = LoginInterceptor.thread.get();
        Long memberId = loginMember.getId();

        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        // 异步获取收货地址列表
        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
            // 线程共享
            RequestContextHolder.setRequestAttributes(requestAttributes);
            // 远程查询用户所有收货地址列表
            List<MemberAddressVo> addressVos = memberFeignService.getMemberAddressList(memberId);
            confirmVo.setAddress(addressVos);

        }, executor);

        // 异步获取购物项列表
        CompletableFuture<Void> itemFuture = CompletableFuture.runAsync(() -> {
            // 线程共享
            RequestContextHolder.setRequestAttributes(requestAttributes);
            // 远程查询用户选中的购物车项
            List<OrderCartItem> checkItems = cartFeignService.getCheckItems(memberId);
            confirmVo.setItems(checkItems);
            // 计算订单总价
            BigDecimal totalPrice = new BigDecimal(0);
            int count = 0;
            for (OrderCartItem checkItem : checkItems) {
                totalPrice = totalPrice.add(checkItem.getTotalPrice());
                count += checkItem.getCount();
            }
            // 设置其他属性
            confirmVo.setCount(count);
            confirmVo.setTotalPrice(totalPrice);
            // TODO 计算减免价格
            confirmVo.setPayPrice(totalPrice);

        }, executor).thenRunAsync(() -> {
            // 获取购物项列表
            List<OrderCartItem> items = confirmVo.getItems();
            // 获取购物项的skuId集合
            List<Long> skuIds = items.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
            // 远程调用查询库存信息
            List<HasStockTo> hasStockTos = wareFeignService.hasStock(skuIds);
            // 将每件的sku是否含有库存封装成map形式，key为skuId，value为是否含有库存
            Map<Long, Boolean> map = hasStockTos.stream().collect(Collectors.toMap(HasStockTo::getSkuId, HasStockTo::getHasStock));
            confirmVo.setStocks(map);

        },executor);

        // 等待两个任务完成
        CompletableFuture.allOf(addressFuture,itemFuture).get();

        // 获取用户的积分信息
        confirmVo.setIntegration(loginMember.getIntegration());

        // 随时生成一个token
        String token = UUID.randomUUID().toString().replaceAll("-", "");
        confirmVo.setOrderToken(token);
        // 存储到服务器  key = prefix + userId + token   value = token
        stringRedisTemplate.opsForValue().set(OrderConstant.getOrderToken(memberId) + token,token);

        return confirmVo;
    }

    /**
     * 创建订单
     * @param vo
     * @return
     */
    @Override
    @Transactional
    public SubmitOrderResponseVo createSubmitOrder(OrderSubmitVo vo) throws ExecutionException, InterruptedException {

        // 当前线程共享传递过来的订单信息
        orderSubmitVoThreadLocal.set(vo);
        // 返回值对象
        SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo();

        // 获取当前线程对象的用户信息
        MemberResponseVo loginMember = LoginInterceptor.thread.get();
        Long memberId = loginMember.getId();

        // 1.验证令牌是否合法【对比和删除必须保持原子性】，使用lua脚本执行验证与删除
        // lua脚本
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        String orderToken = vo.getOrderToken();     //用户提交的token令牌
        String tokenKey = OrderConstant.getOrderToken(memberId) + orderToken;
        // 执行脚本，获取返回值结果
        Long res = stringRedisTemplate.execute(new DefaultRedisScript<Long>(script,Long.class), Arrays.asList(tokenKey), orderToken);

        if (res == 0L){
            // 验证失败
            responseVo.setCode(1);
            return responseVo;
        }

        // 2.令牌验证成功，创建订单
        OrderCreateTo order = createOrder();

        // 3.验证用户提交的价格和实际计算购物车选中的购物项总价是否有出入
        BigDecimal userSubmitPrice = vo.getPayPrice();    //用户提交的总价，是加了运费的
        BigDecimal fare = order.getFare();     //运费
        BigDecimal payPrice = order.getPayPrice();
        BigDecimal subtract = userSubmitPrice.subtract(fare);
        if (Math.abs(subtract.subtract(payPrice).doubleValue()) > 0.01){
            // 验价失败
            responseVo.setCode(2);
            return responseVo;
        }

        // 4.金额验证成功，保存订单数据
        saveOrder(order);
        // 5.保存订单，锁定库存信息，只要有异常则事务回滚
        WareSkuLockVo wareSkuLockVo = new WareSkuLockVo();
        wareSkuLockVo.setOrderSn(order.getOrder().getOrderSn());
        List<OrderCartItem> lockStock = order.getOrderItems().stream().map(item -> {
            OrderCartItem cartItem = new OrderCartItem();
            cartItem.setSkuId(item.getSkuId());
            cartItem.setCount(item.getSkuQuantity());
            return cartItem;
        }).collect(Collectors.toList());

        wareSkuLockVo.setLocks(lockStock);

        R lockRes = wareFeignService.lockSkuStock(wareSkuLockVo);

        if (lockRes.getCode() != 0){
            //库存锁定失败
            responseVo.setCode(3);
            return responseVo;
        }

        responseVo.setOrder(order.getOrder());
        responseVo.setCode(0);

        return responseVo;
    }

    /**
     * 查询订单的状态信息
     * @param orderSn
     * @return
     */
    @Override
    public OrderStatusTo getOrderStatus(String orderSn) {
        OrderStatusTo statusTo = new OrderStatusTo();
        OrderEntity orderEntity = this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
        BeanUtils.copyProperties(orderEntity,statusTo);
        return statusTo;
    }

    /**
     * 取消订单操作
     * @param orderSn
     * @return
     */
    @Override
    public Boolean cancelOrder(String orderSn) {
        // 1.首先根据订单号去查询订单的此时状态
        OrderEntity orderEntity = this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
        // 2.订单状态为待付款状态，则将订单状态修改为已取消状态
        if (orderEntity.getStatus() == OrderStatusEnum.CREATE_NEW.getCode()){
            orderEntity.setStatus(OrderStatusEnum.CANCLED.getCode());
            boolean res = this.updateById(orderEntity);
            // 返回修改状态，修改成功为true，失败为false
            return res;
        }else {
            // 否则返回false
            return false;
        }
    }

    /**
     * 创建支付对象
     * @param orderSn
     * @return
     */
    @Override
    public PayVo aliPayForOrder(String orderSn) {
        PayVo vo = new PayVo();
        // 1.根据订单id查询订单信息
        OrderEntity orderEntity = this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
        vo.setTotal_amount(orderEntity.getTotalAmount().setScale(2,BigDecimal.ROUND_UP).toString());
        vo.setOut_trade_no(orderSn);
        List<OrderItemEntity> entities = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", orderSn));
        vo.setBody(entities.get(0).getSkuName());
        vo.setSubject("谷粒商城支付中...");
        return vo;
    }

    /**
     * 查询用户的订单列表信息
     * @return
     */
    @Override
    public List<OrderListVo> listMemberOrderList() {
        // 获取当前线程的用户信息
        MemberResponseVo member = LoginInterceptor.thread.get();
        // 查询当前用户的所有订单信息
        List<OrderEntity> orderEntities = this.list(new QueryWrapper<OrderEntity>().eq("member_id", member.getId()));

        // 抽取订单的订单号列表
        List<OrderListVo> listVos = orderEntities.stream().map(item -> {
            String orderSn = item.getOrderSn();
            List<OrderItemEntity> items = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", orderSn));
            OrderListVo vo = new OrderListVo();
            BeanUtils.copyProperties(item,vo);
            vo.setItems(items);
            return vo;
        }).collect(Collectors.toList());

        return listVos;
    }

    /**
     * 处理支付结果
     * @param vo
     */
    @Override
    @Transactional
    public String handlerPayResult(PayAsyncVo vo) {
        // 1.保存交易流水信息oms_payment_info
        PaymentInfoEntity payInfo = new PaymentInfoEntity();

        String orderSn = vo.getOut_trade_no();
        payInfo.setOrderSn(orderSn);   //商户订单号
        payInfo.setAlipayTradeNo(vo.getTrade_no()); //支付宝流水号
        payInfo.setCreateTime(vo.getGmt_create());  //流水创建时间
        payInfo.setSubject(vo.getSubject());    //交易内容
        payInfo.setTotalAmount(new BigDecimal(vo.getTotal_amount()));   //订单支付总额
        payInfo.setCallbackContent(vo.getNotify_type());   //回调内容，设置为异步回调类型
        payInfo.setCallbackTime(vo.getNotify_time());   //异步回调的时间
        payInfo.setConfirmTime(new Date()); //订单确认支付时间，新建
        payInfo.setPaymentStatus(vo.getTrade_status()); //订单状态

        // 2.执行插入操作
        paymentInfoService.save(payInfo);

        // 3.修改订单的状态
        if (vo.getTrade_status().equals("TRADE_SUCCESS") || vo.getTrade_status().equals("TRADE_FINISHED")){
            // 订单状态为交易成功或者交易完成，修改订单状态
            baseMapper.updateOrderStatus(orderSn,OrderStatusEnum.PAYED.getCode());
        }

        return "success";

    }

    /**
     * 订单保存到数据库中
     * @param createTo
     */
    private void saveOrder(OrderCreateTo createTo) {
        OrderEntity orderEntity = createTo.getOrder();
        List<OrderItemEntity> orderItems = createTo.getOrderItems();
        this.save(orderEntity);
        orderItemService.saveBatch(orderItems);
    }


    /**
     * 创建订单     TODO 已经初步优化
     * @return
     */
    private OrderCreateTo createOrder() throws ExecutionException, InterruptedException {

        // 获取当前线程对象的用户信息
        MemberResponseVo loginMember = LoginInterceptor.thread.get();
        Long memberId = loginMember.getId();
        OrderCreateTo createTo = new OrderCreateTo();
        OrderSubmitVo submitVo = orderSubmitVoThreadLocal.get();

        // 2.设置订单的订单号
        String orderSn = IdWorker.getTimeId();

        OrderEntity order = createOrderBaseInfo(submitVo);
        order.setOrderSn(orderSn);

        // 创建查询订单项以及构建订单项的异步任务
        CompletableFuture<List<OrderItemEntity>> itemFuture = CompletableFuture.supplyAsync(() -> {
            // 3.查询所有的订单项内容
            List<OrderCartItem> items = cartFeignService.getCheckItems(memberId);
            // 4.构建订单项内容
            List<OrderItemEntity> orderItems = items.stream().map(item -> {
                OrderItemEntity orderItem = buildOrderItem(item);
                orderItem.setOrderSn(orderSn);
                return orderItem;
            }).collect(Collectors.toList());
            return orderItems;
        }, executor);

        List<OrderItemEntity> orderItems = itemFuture.get();

        // 5.计算订单项的总价
        BigDecimal payPrice = new BigDecimal("0");
        Integer totalGift = 0;  //计算总的成长值
        Integer totalGiftIntegration = 0;   //成长积分

        for (OrderItemEntity item : orderItems) {
            payPrice = payPrice.add(item.getRealAmount());  //订单总价
            totalGift += item.getGiftGrowth();  //成长值
            totalGiftIntegration += item.getGiftIntegration();  //成长积分
        }

        order.setTotalAmount(payPrice);     //订单总价格
        order.setPayAmount(payPrice);       //应付金额，此处设置和订单总额一致，后期需修改（不想改了，懒）

        order.setGrowth(totalGift);     //设置总的成长值
        order.setIntegration(totalGiftIntegration); //设置总的成长积分

        BigDecimal fare = order.getFreightAmount();     //运费

        createTo.setOrderItems(orderItems); //订单项
        createTo.setPayPrice(payPrice); //订单项的总应该支付额
        createTo.setOrder(order);   //订单基本信息
        createTo.setFare(fare); //运费

        return createTo;
    }

    /**
     *  构建订单项内容
     * @param item
     * @return
     */
    private OrderItemEntity buildOrderItem(OrderCartItem item) {

        ObjectMapper mapper = new ObjectMapper();
        OrderItemEntity orderItem = new OrderItemEntity();
        // 1.订单基本信息

        // 2.spu信息
        R res = productFeignService.getSpuInfoBySkuId(item.getSkuId());
        SpuInfoVo infoVo = mapper.convertValue(res.get("spuInfo"), SpuInfoVo.class);
        orderItem.setSpuId(infoVo.getId());
        orderItem.setSpuName(infoVo.getSpuName());
        orderItem.setSpuPic(infoVo.getSpuDescription());
        orderItem.setSpuBrand(infoVo.getBrandId().toString());
        orderItem.setCategoryId(infoVo.getCatalogId());

        // 3.sku信息
        orderItem.setSkuId(item.getSkuId());       //skuID
        orderItem.setSkuName(item.getTitle());     //sku名字
        orderItem.setSkuPic(item.getImage());  //sku图片地址
        orderItem.setSkuPrice(item.getPrice());    //sku价格
        String attr = StringUtils.collectionToDelimitedString(item.getSkuAttr(), ";");
        orderItem.setSkuAttrsVals(attr);    //sku的销售属性
        orderItem.setSkuQuantity(item.getCount()); //sku数量

        // 4.优惠信息
        BigDecimal freePrice = item.getPrice().divide(new BigDecimal(10));  //价格的十分之一为优惠价格
        orderItem.setPromotionAmount(new BigDecimal(0));    //0
        orderItem.setCouponAmount(new BigDecimal(0));   //优惠券没做，暂时为0
        orderItem.setIntegrationAmount(new BigDecimal(0));  //按照积分应该查询的 TODO 暂时设置为0

        // 5.积分信息
        orderItem.setGiftIntegration(freePrice.intValue());     //赠送积分，价格的十分之一
        orderItem.setGiftGrowth(freePrice.intValue());  //赠送成长值，价格的十分之一

        // 6.订单项优惠之后的价格
        BigDecimal finalPrice = item.getPrice().subtract(freePrice);
        orderItem.setRealAmount(item.getPrice().multiply(new BigDecimal(item.getCount()))); //订单总额等于订单单价 * 数量

        return orderItem;
    }

    /**
     * 创建订单的基本信息    TODO 已经初步优化 速度有所提升
     * @param submitVo
     * @return
     */
    private OrderEntity createOrderBaseInfo(OrderSubmitVo submitVo) throws ExecutionException, InterruptedException {

        ObjectMapper mapper = new ObjectMapper();
        OrderEntity order = new OrderEntity();
        MemberResponseVo member = LoginInterceptor.thread.get();
        // 获取收货地址的id，当收货地址id为null时，查询用户的默认地址
        Long addrId = submitVo.getAddrId();

        // 创建一个查询用户地址的异步任务

        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {

            // 根据地址的id查询地址的详细信息
            R res = memberFeignService.getAddress(addrId);
            MemberAddressVo address = mapper.convertValue(res.get("memberReceiveAddress"), MemberAddressVo.class);

            order.setMemberId(member.getId());
            order.setMemberUsername(member.getUsername());

            // 设置订单的收货人地址信息
            order.setReceiverCity(address.getCity());        //收货城市
            order.setReceiverDetailAddress(address.getDetailAddress());   //详细地址
            order.setReceiverName(address.getName());    //收货人
            order.setReceiverPhone(address.getPhone());   //手机号
            order.setReceiverPostCode(address.getPostCode());    //邮编
            order.setReceiverProvince(address.getProvince());    //省份
            order.setReceiverRegion(address.getRegion());  //区域

            // 订单状态
            order.setStatus(OrderStatusEnum.CREATE_NEW.getCode());

        }, executor);

        // 创建一个根据地址id查询地址的运费
        CompletableFuture<Void> fareFuture = CompletableFuture.runAsync(() -> {
            // 查询运费
            R fareRes = wareFeignService.getFareInfo(addrId);
            Object fareObj = fareRes.get("fare");
            BigDecimal fare = new BigDecimal(fareObj.toString());
            order.setFreightAmount(fare);    //设置运费
            order.setCreateTime(new Date());
        }, executor);

        // 异步阻塞等待两个异步任务完成
        CompletableFuture<Void> future = CompletableFuture.allOf(addressFuture, fareFuture);
        future.get();

        return order;
    }

}